package com.abysscore.yachtdesign;

import java.util.Vector;
/**
 * TFreeHydrostaticCalc                                        
 * TFreeHydrostaticCalc is an object class for hydrostatic calculations.                           
 * Each calculation has it's own draft, trim and angle of heel.                                    
 * Multiple calculations can be stored and then send to a report.
 * @author kkolehma
 *
 */
public class TFreeHydrostaticCalc {
	private double CosHeel;
	private double SinHeel;
	private double CosTrim;
	private double SinTrim;
	private T3DCoordinate NewOrigin;
	private TFreeShip FOwner;
    // Input data for each calculation
	private double FHeelingAngle;
	private double FTrim;
	private double FDraft;
    // Calculation flags
	private boolean FCalculated;
	private Vector<TFreeHydrostaticError> FErrors;
	private TFreeHydrostaticType FHydrostaticType; // Determines how calculations are performed: short, extensive etc.
    // The following data is calculated
	private TFreeHydrostaticsData FData;
	private Vector<TFreeHydrostaticsCalculation> FCalculations;
	private TFreeIntersection FMainFrame;
	
	
	private double FGetTrimAngle() {
		return 0;
	}
	private T3DPlane FGetWlPlane() {
		return null;
	}
	private void FSetCalculated(boolean val) {
	}
	private void FSetDraft(double Val) {
	}
	private void FSetErrors(Vector<TFreeHydrostaticError> val) {
		FErrors = val;
	}
	private void FSetHeelingAngle(double Val) {
	}
	private void FSetHydrostaticType(TFreeHydrostaticType val) {
	}
	private void FSetTrim(double Val) {
	}
	
	
	public boolean Balance(double Displacement, boolean FreeToTrim, TFreeCrosscurvesData Output) {
		return false;
	}
    
	public void ShowData(TFreeHydrostaticsMode Mode) {
	}
	
	public boolean Calculated() { return FCalculated; }
	public void Calculated(boolean val) { FSetCalculated(val); }
	public Vector<TFreeHydrostaticsCalculation> Calculations() { return FCalculations; }
	public void Calculations(Vector<TFreeHydrostaticsCalculation> val) { FCalculations = val; }
	public TFreeHydrostaticsData Data()  { return FData; }
	public double Draft() { return FDraft; }
	public void Draft(double val) { FSetDraft(val); }
	public Vector<TFreeHydrostaticError> Errors() { return FErrors; }
	public void Errors(Vector<TFreeHydrostaticError> val) { FSetErrors(val); }
	public String ErrorString() { return FGetErrorString(); }
	public double HeelingAngle() { return FHeelingAngle; }
	public void HeelingAngle(double val) { FSetHeelingAngle(val); }
	public TFreeHydrostaticType HydrostaticType() { return FHydrostaticType; }
	public void HydrostaticType(TFreeHydrostaticType val) { FSetHydrostaticType(val); } // Determines how calculations are performed: short, extensive etc.
	public TFreeShip Owner() { return FOwner; }
	public double Trim() { return FTrim; }
	public void Trim(double val) { FSetTrim(val); }
	public double TrimAngle() { return FGetTrimAngle(); }
	public T3DPlane WaterlinePlane() { return FGetWlPlane(); }

	TFreeHydrostaticError error; // Errors that may occur when calculating hydrostatics
	
	public TFreeHydrostaticCalc(TFreeShip Owner){
		
	}
	/**
	 * // Add calculated data to a stringlist to either show in a report or save to disc
	 * @param Strings
	 * @param Mode
	 * @param Separator
	 */
	public void AddData(Vector<String> Strings, TFreeHydrostaticsMode Mode, char Separator)
	{
		
	}
	public void AddHeader(Vector<String> Strings){
		
	}
	public void AddFooter(Vector<String> Strings, TFreeHydrostaticsMode Mode){
		
	}
	public TFreeCrosscurvesData Balance(double Displacement, boolean FreeToTrim){
		return null;
	}
	/**
	 * The actual calculation of the hydrostatics finds place in this void
	 */
	public void Calculate(){
		
	}
	
	private T3DCoordinate RotatePoint(T3DCoordinate P, T3DCoordinate Keel)
	{
		T3DCoordinate Result = new T3DCoordinate();
		P.Z = P.Z - Keel.Z;
		Result.X = P.X*CosTrim+P.Y*SinHeel*SinTrim+P.Z*CosHeel*SinTrim;
		Result.Y = P.Y*CosHeel-P.Z*SinHeel;
		Result.Z = -P.X*SinTrim+P.Y*SinHeel*CosTrim+P.Z*CosHeel*CosTrim;
		return Result;
	}
	private T3DCoordinate RotatePoint(T3DCoordinate P)
    // Rotate a point at heel=0 and trim=0 position to given trim and heel
	{
		return RotatePoint(P, new T3DCoordinate(0,0,0));
	}
	private void ProcessTriangle(T3DCoordinate P1,T3DCoordinate P2, T3DCoordinate P3)
	{
		T3DCoordinate VolumeMoment = new T3DCoordinate();
        double Volume;
        T3DCoordinate Center = new T3DCoordinate();

        // Reposition points with respect to the new projected origin
        P1.X = P1.X-NewOrigin.X;
        P1.Y = P1.Y-NewOrigin.Y;
        P1.Z = P1.Z-NewOrigin.Z;
        P2.X = P2.X-NewOrigin.X;
        P2.Y = P2.Y-NewOrigin.Y;
        P2.Z = P2.Z-NewOrigin.Z;
        P3.X = P3.X-NewOrigin.X;
        P3.Y = P3.Y-NewOrigin.Y;
        P3.Z = P3.Z-NewOrigin.Z;
        Center.X = (P1.X+P2.X+P3.X)/3;
        Center.Y = (P1.Y+P2.Y+P3.Y)/3;
        Center.Z = (P1.Z+P2.Z+P3.Z)/3;
        Volume = ((P1.Z)*(P2.X*P3.Y-P2.Y*P3.X)+
                (P1.Y)*(P2.Z*P3.X-P2.X*P3.Z)+
                (P1.X)*(P2.Y*P3.Z-P2.Z*P3.Y))/6;
       if (Volume!=0)
       {
          VolumeMoment.X = 0.75*Center.X*Volume;
          VolumeMoment.Y = 0.75*Center.Y*Volume;
          VolumeMoment.Z = 0.75*Center.Z*Volume;
          FData.Volume = FData.Volume+Volume;
          FData.CenterOfBuoyancy.X = FData.CenterOfBuoyancy.X+VolumeMoment.X;
          FData.CenterOfBuoyancy.Y = FData.CenterOfBuoyancy.Y+VolumeMoment.Y;
          FData.CenterOfBuoyancy.Z = FData.CenterOfBuoyancy.Z+VolumeMoment.Z;
       }
	}
	
	public void CalculateVolume(T3DPlane WaterlinePlane) {
		int I,J,K,L;
		double Parameter;
		TFREESubdivisionLayer Layer;
		TFreeSubdivisionControlFace Face;
		TFreeSubdivisionFace Child;
		double Side1,Side2;
	    T3DCoordinate P1,P2;
	    T3DCoordinate P = new T3DCoordinate();
	    T3DCoordinate Keel = new T3DCoordinate();
	    
	    boolean FirstPoint;
	    boolean Submerged;
	    Vector<T3DCoordinate> Points = new Vector();  // Array containing the coordinates of the underwaterpart of a face
	    int PointCapacity;                 // Length of the dynamic point-array
	    int NoPoints;                 // number of points stored int the dynamic array
	    
	    // Initial setup of data
	    CosHeel = Math.cos(Math.toRadians(-HeelingAngle()));
	    SinHeel = Math.sin(Math.toRadians(-HeelingAngle()));
	    CosTrim = Math.cos(Math.toRadians(-TrimAngle()));
	    SinTrim = Math.sin(Math.toRadians(-TrimAngle()));

	    Calculated(false);   // Resets all data and calculated variables
	    // Calculate the definition of the waterlineplane
	    Keel = FOwner.SetPoint(0.0,0.0,Owner().FindLowestHydrostaticsPoint());
	    FData.WaterlinePlane = WaterlinePlane;

	    // In order to calculate the volume enclosed by the underwatership correctly,
	    // the origin (0.0, 0.0, 0.0) is projected onto the waterline plane
	    NewOrigin = FOwner.ProjectPointOnPlane(new T3DCoordinate(0,0,0),FData.WaterlinePlane);

	    // Now calculate the actual volume
	    Submerged = false;
	    PointCapacity = 5;	
	    Points.setSize(PointCapacity);
	    FData.AbsoluteDraft = 1000;
	    FirstPoint = true;
	    for (I = 1; I <=  FOwner.NumberOfLayers(); I++) 
	    {
	      Layer = FOwner.Layer(I-1);
	      if (Layer.UseInHydrostatics())
	    	  for (J=1; J <= Layer.Count(); J++)
	    	  {
	    		  Face = Layer.Items(J-1);
	    		  for (K = 1; K <= Face.ChildCount(); K++)
	    		  {
	    			  Child = Face.Child(K-1);
	    			  // Calculate the portside of the model
	    			  NoPoints = 0;
	    			  P1 = Child.Point(Child.NumberOfpoints()-1).Coordinate();
	    			  // calculate on which side of the waterplane this point is
	    			  Side1 = FData.WaterlinePlane.a*P1.X+FData.WaterlinePlane.b*P1.Y+FData.WaterlinePlane.c*P1.Z+FData.WaterlinePlane.d;
	    			  for (L=1; L <= Child.NumberOfpoints(); L++)
	    			  {
	    				  P2 = Child.Point(L-1).Coordinate();
	    				  Side2 = FData.WaterlinePlane.a*P2.X+FData.WaterlinePlane.b*P2.Y+FData.WaterlinePlane.c*P2.Z+FData.WaterlinePlane.d;
	    				  if (((Side1<-1e-5) && (Side2>1e-5)) || ((Side1>1e-5) && (Side2<-1e-5)))
	    				  { 
	    					  // The current linesegment between P1-P2 intersects the waterlineplae
	    					  if (Side1==Side2)
	    						  Parameter = 0.5*(Side1+Side2);
	    					  else 
	    						  Parameter =-Side1/(Side2-Side1);
	    					  P.X = P1.X+Parameter*(P2.X-P1.X);
	    					  P.Y = P1.Y+Parameter*(P2.Y-P1.Y);
	    					  P.Z = P1.Z+Parameter*(P2.Z-P1.Z);
	    					  NoPoints++;
	    					  if (NoPoints>PointCapacity)
	    					  {
	    						  PointCapacity++;
	    						  Points.setSize(PointCapacity);
	    					  }
	    					  Points.add(NoPoints-1,P);
	    				  }
	    				  if (Side2<=1e-5)
	    				  {
	    					  if (Side2<FData.AbsoluteDraft)
	    						  FData.AbsoluteDraft =Side2;
	    					  // P2 lies also on or under the waterlineplane
	    					  NoPoints++;
	    					  if (NoPoints>PointCapacity)
	    					  {
	    						  PointCapacity++;
	    						  Points.setSize(PointCapacity);
	    					  }
	    					  Points.add(NoPoints-1,P2);
	    					  if (Side2<-1e-5)
	    					  {
	    						  // point is submerged, check if the model is making water
	    						  if ((Child.Point(L-1).IsBoundaryVertex()) && (Math.abs(Child.Point(L-1).Coordinate().Y)>1e-4))
	    						  {
	    							  if (!(FErrors.contains(TFreeHydrostaticError.feMakingWater))) 
	    							  {
	    								  FErrors.add(TFreeHydrostaticError.feMakingWater);
	    								  FData.Leak = Child.Point(L-1).Coordinate();
	    							  }
	    						  }
	    					  }
	    				  }
	    				  P1 = P2;
	    				  Side1 = Side2;
	    			  }

	    			  // calculate volume aft of this face
	    			  if (NoPoints>2)
	    				  Submerged = true;
	    			  for (L=3; L<=NoPoints; L++)
	    				  ProcessTriangle(Points.get(0),Points.get(L-2),Points.get(L-1));

	    			  if (Layer.Symmetric())
	    			  {
	    				  // Calculate the starboardside of the model
	    				  NoPoints = 0;
	    				  P1 = Child.Point(Child.NumberOfpoints()-1).Coordinate();
	    				  P1.Y = -P1.Y;
	    				  // calculate on which side of the waterplane this point is
	    				  Side1 = FData.WaterlinePlane.a*P1.X+FData.WaterlinePlane.b*P1.Y+FData.WaterlinePlane.c*P1.Z+FData.WaterlinePlane.d;
	    				  for (L = 1; L <=  Child.NumberOfpoints(); L++)
	    				  {
	    					  P2 = Child.Point(L-1).Coordinate();
	    					  P2.Y = -P2.Y;
	    					  Side2 = FData.WaterlinePlane.a*P2.X+FData.WaterlinePlane.b*P2.Y+FData.WaterlinePlane.c*P2.Z+FData.WaterlinePlane.d;
	    					  if (((Side1<-1e-5) && (Side2>1e-5)) || ((Side1>1e-5) && (Side2<-1e-5)))
	    					  {
	    						  // The current linesegment between P1-P2 intersects the waterlineplae
	    						  if (Side1==Side2)
	    							  Parameter = 0.5*(Side1+Side2);
	    						  else 
	    							  Parameter = -Side1/(Side2-Side1);
	    						  P.X=P1.X+Parameter*(P2.X-P1.X);
	    						  P.Y=P1.Y+Parameter*(P2.Y-P1.Y);
	    						  P.Z=P1.Z+Parameter*(P2.Z-P1.Z);
	    						  NoPoints++;
	    						  if (NoPoints>PointCapacity)
	    						  {
	    							  PointCapacity++;
	    							  Points.setSize(PointCapacity);
	    						  }
	    						  Points.add(NoPoints-1,P);
	    					  }
	    					  if (Side2<=1e-5)
	    					  {
	    						  // P2 lies also under the waterlineplane
	    						  if(Side2<FData.AbsoluteDraft)
	    							  FData.AbsoluteDraft=Side2;
	    						  NoPoints++;
	    						  if (NoPoints>PointCapacity)
	    						  {
	    							  PointCapacity++;
	    							  Points.setSize(PointCapacity);
	    						  }
	    						  Points.add(NoPoints-1,P2);
	    						  if(Side2<-1e-5)
	    						  {
	    							  // point is submerged, check if the model is making water
	    							  if ((Child.Point(L-1).IsBoundaryVertex()) && (Math.abs(Child.Point(L-1).Coordinate().Y)>1e-4))
	    							  {
	    								  if (!(FErrors.contains(TFreeHydrostaticError.feMakingWater))) 	
	    								  {
	    									  FErrors.add(TFreeHydrostaticError.feMakingWater);
	    									  FData.Leak=Child.Point(L-1).Coordinate();
	    								  }
	    							  }
	    						  }
	    					  }
	    					  P1=P2;
	    					  Side1=Side2;
	    				  }
	    				  // calculate volume aft of this face
	    				  if (NoPoints>2)
	    					  Submerged=true;
	    				  for (L=3; L<=NoPoints; L++)
	    					  ProcessTriangle(Points.get(0),Points.get(L-1),Points.get(L-2));
	    			  }
	    		  }
	    	  }
	    } //for
	    FData.AbsoluteDraft = -FData.AbsoluteDraft;
	    if (FirstPoint)
	    {
	    	// No intersection with the watersurface found, the ship is either
	    	// not submerged or totally submerged
	    	if (!Submerged)
	    	{
	    		FErrors.add(TFreeHydrostaticError.feNothingSubmerged);
	    		FData.AbsoluteDraft=0.0;
	    	}
	    }
	    if (FErrors.contains(TFreeHydrostaticError.feMakingWater))
	    {
	    	FData.Volume=0.0;
	    	FData.CenterOfBuoyancy=new T3DCoordinate(0,0,0);
	    }
	    if (FData.Volume!=0) 
	    {
	    	// Translate center of buoyancy back to the original origin
	    	FData.CenterOfBuoyancy.X=NewOrigin.X+FData.CenterOfBuoyancy.X/FData.Volume;
	    	FData.CenterOfBuoyancy.Y=NewOrigin.Y+FData.CenterOfBuoyancy.Y/FData.Volume;
	    	FData.CenterOfBuoyancy.Z=NewOrigin.Z+FData.CenterOfBuoyancy.Z/FData.Volume;
	    	FData.CenterOfBuoyancy=RotatePoint(FData.CenterOfBuoyancy);
	    	if (FData.LengthWaterline!=0)
	    		FData.LCBPerc=100*((FData.CenterOfBuoyancy.X-FOwner.ProjectSettings().ProjectMainframeLocation())/FData.LengthWaterline);
	    	// Apply appendage coeff.
	    	FData.Displacement=FOwner.VolumeToDisplacement(FData.Volume,FOwner.ProjectSettings().ProjectWaterDensity(),FOwner.ProjectSettings().ProjectAppendageCoefficient(),FOwner.ProjectSettings().ProjectUnits());
	    	FData.Volume=FData.Volume*FOwner.ProjectSettings().ProjectAppendageCoefficient();

	    }
	    FCalculated=true;
	}
	public void Clear() {
		
	}
	
	private String FGetErrorString(){
	   String Result ="ERROR";
	   /*
	   if (TFreeHydrostaticError.feNothingSubmerged == error)
		   Result = Result+UserString(0)+"\n";
	   if (TFreeHydrostaticError.feMakingWater == error)
		   Result = Result+UserString(1)+"#32"+FloatToStrF(FData.Leak.X,ffFixed,7,3)+", "+
	                                           FloatToStrF(FData.Leak.Y,ffFixed,7,3)+", "+
	                                           FloatToStrF(FData.Leak.Z,ffFixed,7,3)+"\n";
	                                           */
	   return Result;
	}
}
